from typing import *
from os import path
from new_rmasm.exceptions import *

from new_rmasm import asts


class TCToken:

    def __init__(self, token: 'LexToken') -> None:
        self.token = token

    def translate(self, env) -> str:
        raise Exception(f"{self.__class__} 不能翻译。")

    def str(self) -> str:
        return 'None'

    def __str__(self):
        return '<' + self.__class__.__name__ + ' ' + self.str() + '>'


class TextTCToken(TCToken):

    def __init__(self, token: 'LexToken', content: str) -> None:
        super().__init__(token)
        self.content = content

    def str(self) -> str:
        return self.content


class TagTCToken(TCToken):

    def __init__(self, token: 'LexToken', name: str):
        super().__init__(token)
        self.name = name

    def str(self) -> str:
        return self.name


class TagNumTCToken(TCToken):

    def __init__(self, token: 'LexToken', direction: str, target: str):
        super().__init__(token)
        self.direction = direction
        self.target = target

    def str(self):
        return f'{self.direction}{self.target}'


class EvalTCToken(TCToken):

    def __init__(self, token: 'LexToken', expr: str) -> None:
        super().__init__(token)
        self.expr = expr

    def str(self) -> str:
        return self.expr


class TempCode:

    def __init__(self, token: 'LexToken', *codes) -> None:
        super().__init__()
        self.token = token
        self.codes = codes
        self.step_line: bool = True
        self.has_result: bool = True

    def __str__(self) -> str:
        res = ' '.join([str(i) for i in self.codes])
        p = []
        if not self.step_line:
            p.append('No Result')
        if not self.has_result:
            p.append('No Step')
        if p:
            return '#' + '|'.join(p) + '# ' + res
        return res

    def translate(self, env) -> str:
        res = []
        for i in self.codes:
            if isinstance(i, str):
                res.append(i)
            elif isinstance(i, TCToken):
                res.append(i.translate(env))
            else:
                raise RTranslateError(
                    self.token.source, self.token.line_num,
                    f"错误，临时代码只能是 TempToken 或字符串。不能是 {i}"
                )
        return ' '.join(res)


class TagTempCode(TempCode):

    def __init__(self, token: 'LexToken', name: str) -> None:
        super().__init__(token, TagTCToken(token, name))
        self.has_result = False
        self.step_line = False


class AstsTranslateEnv:
    def __init__(self):
        super().__init__()
        self.parent: 'AstsTranslateEnv' = None
        self.vars: Dict[str, str] = {}
        self.macros: Dict[Tuple, 'asts.DefineMacroAST'] = {}
        self.warnings: List[RMASMError] = []
        self.tags: Dict[str, str] = {}
        self.imported: List[str] = []

    def new_sub_env(self) -> 'AstsTranslateEnv':
        sub = AstsTranslateEnv()
        sub.parent = self
        sub.warnings = self.warnings
        sub.tags = self.tags
        return sub

    def has_temp_var(self, name: str) -> bool:
        if name in ['false', 'true']:
            return True
        if name in self.vars:
            return True
        if self.parent is None:
            return False
        return self.parent.has_temp_var(name)

    def get_var(self, name: str) -> str:
        if name in ['false', 'true']:
            return name
        if name in self.vars:
            return self.vars[name]
        if self.parent:
            return self.parent.get_var(name)
        return name

    def get_temp_var_num(self) -> int:
        num = len(self.vars)
        if self.parent:
            num += self.parent.get_temp_var_num()
        return num

    def get_tags_num(self) -> int:
        res = len(self.tags)
        return res

    def new_random_tag(self) -> str:
        new_name = f'__rt_{self.get_tags_num()}'
        self.tags[new_name] = new_name
        return new_name

    def new_temp_var(self, var_name: str) -> str:
        if var_name in self.vars:
            raise Exception(f"重复的临时变量 {var_name}")
        ml_name = f'_tv_{self.get_temp_var_num()}'
        self.vars[var_name] = ml_name
        return ml_name

    def add_war(self, message: RMASMError):
        self.warnings.append(message)

    def new_macro(self, define_ast: 'asts.DefineMacroAST'):
        key = (define_ast.name, len(define_ast.args))
        if key in self.macros:
            self.add_war(
                RTranslateError(define_ast.token.source,
                                define_ast.token.line_num,
                                f"同名同参数数量的宏定义会被覆盖：{define_ast.name}"))
        self.macros[key] = define_ast

    def has_macro(self, name: str, args_num: int) -> bool:
        key = (name, args_num)
        if key in self.macros:
            return True
        if self.parent:
            return self.parent.has_macro(*key)
        return False

    def get_macro(self, name: str, args_num: int) -> 'asts.DefineMacroAST':
        key = (name, args_num)
        if key in self.macros:
            return self.macros[key]
        if self.parent:
            return self.parent.get_macro(*key)
        raise Exception(f"{name} 宏不存在或不支持 {args_num} 个参数。")

    def add_imported(self, file_path: str):
        abspath = path.abspath(file_path)
        self.imported.append(abspath)

    def has_imported(self, file_path: str) -> bool:
        abspath = path.abspath(file_path)
        if abspath in self.imported:
            return True
        if self.parent:
            return self.parent.has_imported(file_path)
        return False
